package utilities.runtime;

import java.io.File;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Vector;

public class loader 
{
	@SuppressWarnings("unchecked")
	protected HashMap      modules;
	protected Vector<File> files;
	
	protected URL getFileURL(File file)
	{
		URL url;
		
		try {
			url = file.toURI().toURL();
		} catch (MalformedURLException e) {
			e.printStackTrace();
			return null;
		}
		
		return url;
	}
	
	@SuppressWarnings("unchecked")
	protected Class getClassFromFile(File file, URLClassLoader child)
	{
		Class loadedClass;
		
		try {
			loadedClass = Class.forName(file.getName().replace(".jar", ""), true, child);
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
			return null;
		}
		
		return loadedClass;
	}
	
	@SuppressWarnings("unchecked")
	protected Object getObjectFromClass(Class loadedClass)
	{
		Object instance;

		try {
			instance = loadedClass.newInstance();
		} catch (InstantiationException e) {
			e.printStackTrace();
			return null;
		} catch (IllegalAccessException e) {
			e.printStackTrace();
			return null;
		}
		
		return instance;
	}
	
	@SuppressWarnings("unchecked")
	protected void invokeTo(Class loadedClass, Object instance, utilities.events.event event)
	{
		Method method;
		
		try {
			method = loadedClass.getMethod("messageReceived", utilities.events.event.class);
			method.invoke(instance, event);
		} catch (Exception e) { e.printStackTrace(); }
	}
	
	@SuppressWarnings("unchecked")
	public loader()
	{
		modules = new HashMap();
		files   = new Vector<File>();
	}
	
	@SuppressWarnings("unchecked")
	public boolean add(String filename)
	{
		File file = new File(filename);
		if (file.exists() && file.getName().endsWith(".jar"))
		{
			files.add(file);
			return true;
		} else
			return false;
	}
	
	public boolean addDirectory(String path)
	{
		File directory = new File(path);
		if (directory.exists())
		{
			File[] files = directory.listFiles();
			for(int i=0; i < files.length; i++)
			{
				if (files[i].getName().endsWith(".jar"))
					add(files[i].getAbsolutePath());
			}
			return true;
		} else
			return false;
	}
	
	@SuppressWarnings("unchecked")
	public void load()
	{
		URL[] urls = new URL[1];
		Iterator<File> itr = files.iterator();
		while(itr.hasNext())
		{
			File file = itr.next();
			urls[0] = getFileURL(file);
			
			URLClassLoader child = new URLClassLoader(urls, this.getClass().getClassLoader());
			Class loadedClass = getClassFromFile(file, child);
			Object instance   = getObjectFromClass(loadedClass);
			
			modules.put(loadedClass, instance);
			
			Method method;
			
			try {
				method = loadedClass.getMethod("moduleLoaded");
				method.invoke(instance);
			} catch (SecurityException e) { e.printStackTrace(); continue; } catch (NoSuchMethodException e) { e.printStackTrace(); continue; }
				catch (IllegalArgumentException e) { e.printStackTrace(); continue; } catch (IllegalAccessException e) { e.printStackTrace(); continue; } 
				catch (InvocationTargetException e) { e.printStackTrace(); continue; }
		}
	}
	
	@SuppressWarnings("unchecked")
	public void invoke(utilities.events.event event)
	{	
		Iterator itr = modules.keySet().iterator();
		while(itr.hasNext())
		{
			Class loadedClass = (Class)itr.next();
			Object instance   = modules.get(loadedClass);
			
			invokeTo(loadedClass, instance, event);
		}
	}
	
	@SuppressWarnings("unchecked")
	public boolean invoke(utilities.events.event event, String className)
	{
		Iterator itr = modules.keySet().iterator();
		while(itr.hasNext())
		{
			Class loadedClass = (Class)itr.next();
			String loadedClassName = loadedClass.toString().replace("class ", "").trim();

			if (loadedClassName.equals(className))
			{
				Object instance   = modules.get(loadedClass);
				invokeTo(loadedClass, instance, event);
				
				return true;
			}
		}
		
		return false;
	}
	
	@SuppressWarnings("unchecked")
	public HashMap getModules()
	{
		return modules;
	}
}
